#include "storescpthread.h"
#include "../share/configfiles.h"
#include "../MainStation/logdbmanager.h"
#include "../MainStation/mainwindow.h"
#include "../MainStation/studydbmanager.h"
#include "../share/studyrecord.h"

#include <QDir>

StoreScpThread::StoreScpThread(QObject *parent) :
    abort(false),
    QThread(parent)
{
}

#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */

#define INCLUDE_CSTDLIB
#define INCLUDE_CSTRING
#define INCLUDE_CSTDARG
#define INCLUDE_CCTYPE
#define INCLUDE_CSIGNAL
#include "dcmtk/ofstd/ofstdinc.h"

BEGIN_EXTERN_C
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>       /* needed on Solaris for O_RDONLY */
#endif
#ifdef HAVE_SIGNAL_H
// On Solaris with Sun Workshop 11, <signal.h> declares signal() but <csignal> does not
#include <signal.h>
#endif
END_EXTERN_C

#ifdef HAVE_GUSI_H
#include <GUSI.h>
#endif

#ifdef HAVE_WINDOWS_H
#include <direct.h>      /* for _mkdir() */
#endif

#include "dcmtk/ofstd/ofstd.h"
#include "dcmtk/dcmnet/cond.h"
#include "dcmtk/ofstd/ofdatime.h"
#include "dcmtk/dcmnet/dicom.h"         /* for DICOM_APPLICATION_ACCEPTOR */
#include "dcmtk/dcmnet/dimse.h"
#include "dcmtk/dcmnet/diutil.h"
#include "dcmtk/dcmnet/dcasccfg.h"      /* for class DcmAssociationConfiguration */
#include "dcmtk/dcmnet/dcasccff.h"      /* for class DcmAssociationConfigurationFile */
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcuid.h"
#include "dcmtk/dcmdata/dcdict.h"
#include "dcmtk/dcmsr/dsrdoc.h"
#include "dcmtk/dcmdata/dcmetinf.h"
#include "dcmtk/dcmdata/dcuid.h"        /* for dcmtk version name */
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcostrmz.h"     /* for dcmZlibCompressionLevel */

static OFCondition acceptUnknownContextsWithPreferredTransferSyntaxes(
         T_ASC_Parameters * params,
         const char* transferSyntaxes[],
         int transferSyntaxCount,
         T_ASC_SC_ROLE acceptedRole = ASC_SC_ROLE_DEFAULT);
static void insertImageToDB(DcmFileFormat *ff, StudyRecord *study, QString &patientName);

void StoreScpThread::run()
{
  T_ASC_Network *net;
  DcmAssociationConfiguration asccfg;
  OFString temp_str;

#ifdef HAVE_WINSOCK_H
  WSAData winSockData;
  /* we need at least version 1.1 */
  WORD winSockVersionNeeded = MAKEWORD( 1, 1 );
  WSAStartup(winSockVersionNeeded, &winSockData);
#endif

  /* initialize network, i.e. create an instance of T_ASC_Network*. */
  int port = mainWindow->getStationInfo().storeScpPort;
  OFCondition cond = ASC_initializeNetwork(NET_ACCEPTOR, port, 30, &net);
  if (cond.bad())
  {
      DimseCondition::dump(temp_str, cond);
      LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, tr("cannot create network: %1.").arg(temp_str.c_str()));
  }

  while (cond.good() && (!abort))
  {
    /* receive an association and acknowledge or reject it. If the association was */
    /* acknowledged, offer corresponding services and invoke one or more if required. */
    cond = acceptAssociation(net, asccfg);
  }

  /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */
  /* is the counterpart of ASC_initializeNetwork(...) which was called above. */
  if (cond.good()) cond = ASC_dropNetwork(&net);
  if (cond.bad())
  {
    DimseCondition::dump(temp_str, cond);
    LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
  }

#ifdef HAVE_WINSOCK_H
  WSACleanup();
#endif
}


OFCondition StoreScpThread::acceptAssociation(T_ASC_Network *net, DcmAssociationConfiguration& asccfg)
{
  char buf[BUFSIZ];
  T_ASC_Association *assoc;
  OFCondition cond;
  OFString temp_str;

  const char* knownAbstractSyntaxes[] =
  {
    UID_VerificationSOPClass
  };

  const char* transferSyntaxes[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  int numTransferSyntaxes = 0;

  // try to receive an association. Here we either want to use blocking or
  // non-blocking, depending on if the option --eostudy-timeout is set.
  cond = ASC_receiveAssociation(net, &assoc, ASC_DEFAULTMAXPDU);

  // if some kind of error occured, take care of it
  if (cond.bad())
  {
      DimseCondition::dump(temp_str, cond);
      LogDbManager::insertMessageToDb(LogDbManager::ET_SysError,
                                      tr("Receiving Association failed: %1.").arg(temp_str.c_str()));
  }

    /* We prefer explicit transfer syntaxes.
     * If we are running on a Little Endian machine we prefer
     * LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
     */
    if (gLocalByteOrder == EBO_LittleEndian)  /* defined in dcxfer.h */
    {
      transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
      transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
    }
    else
    {
      transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
      transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
    }
    transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
    numTransferSyntaxes = 3;

    /* accept the Verification SOP Class if presented */
    if (cond.good()) {
        cond = ASC_acceptContextsWithPreferredTransferSyntaxes( assoc->params, knownAbstractSyntaxes, DIM_OF(knownAbstractSyntaxes), transferSyntaxes, numTransferSyntaxes);
        if (cond.bad())
        {
            DimseCondition::dump(temp_str, cond);
            LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
        }
    }

    /* the array of Storage SOP Class UIDs comes from dcuid.h */
    if (cond.good()) {
        cond = ASC_acceptContextsWithPreferredTransferSyntaxes( assoc->params, dcmAllStorageSOPClassUIDs, numberOfAllDcmStorageSOPClassUIDs, transferSyntaxes, numTransferSyntaxes);
        if (cond.bad())
        {
            DimseCondition::dump(temp_str, cond);
            LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
        }
    }

  /* set our app title */
  QString aetitle = mainWindow->getStationInfo().aetitle;
  if (aetitle.isEmpty()) aetitle = DEFAULT_STATION_AET;
  ASC_setAPTitles(assoc->params, NULL, NULL, aetitle.toLocal8Bit().data());

  /* acknowledge or reject this association */
  if (cond.good()) {
      cond = ASC_getApplicationContextName(assoc->params, buf);
      if ((cond.bad()) || strcmp(buf, UID_StandardApplicationContext) != 0)
      {
        /* reject: the application context name is not supported */
        T_ASC_RejectParameters rej =
        {
          ASC_RESULT_REJECTEDPERMANENT,
          ASC_SOURCE_SERVICEUSER,
          ASC_REASON_SU_APPCONTEXTNAMENOTSUPPORTED
        };

        DimseCondition::dump(temp_str, cond);
        LogDbManager::insertMessageToDb(LogDbManager::ET_SysError,
                                        tr("Association Rejected: Bad Application Context Name: %1.").arg(buf));
        cond = ASC_rejectAssociation(assoc, &rej);
        if (cond.bad())
        {
            DimseCondition::dump(temp_str, cond);
            LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
        }
      }
      else
      {
        cond = ASC_acknowledgeAssociation(assoc);
        if (cond.bad())
        {
            DimseCondition::dump(temp_str, cond);
            LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
        }
      }
  }

  if (cond.good()) {
      // store calling and called aetitle in global variables to enable
      // the --exec options using them. Enclose in quotation marks because
      // aetitles may contain space characters.
      DIC_AE callingTitle;
      DIC_AE calledTitle;
      ASC_getAPTitles(assoc->params, callingTitle, calledTitle, NULL);

      /* now do the real work, i.e. receive DIMSE commmands over the network connection */
      /* which was established and handle these commands correspondingly. In case of */
      /* storscp only C-ECHO-RQ and C-STORE-RQ commands can be processed. */
      cond = processCommands(assoc);

      if (cond == DUL_PEERREQUESTEDRELEASE)
      {
        cond = ASC_acknowledgeRelease(assoc);
      }
      else
      {
        DimseCondition::dump(temp_str, cond);
        LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, tr("DIMSE failure (aborting association): %1.").arg(temp_str.c_str()));
        /* some kind of error so abort the association */
        cond = ASC_abortAssociation(assoc);
      }
  }

  cond = ASC_dropSCPAssociation(assoc);
  if (cond.bad())
  {
      DimseCondition::dump(temp_str, cond);
      LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
  }
  cond = ASC_destroyAssociation(&assoc);
  if (cond.bad())
  {
      DimseCondition::dump(temp_str, cond);
      LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QString(temp_str.c_str()));
  }

  return cond;
}


OFCondition StoreScpThread::processCommands(T_ASC_Association * assoc)
    /*
     * This function receives DIMSE commmands over the network connection
     * and handles these commands correspondingly. Note that in case of
     * storscp only C-ECHO-RQ and C-STORE-RQ commands can be processed.
     *
     * Parameters:
     *   assoc - [in] The association (network connection to another DICOM application).
     */
{
  OFCondition cond = EC_Normal;
  T_DIMSE_Message msg;
  T_ASC_PresentationContextID presID = 0;
  DcmDataset *statusDetail = NULL;

  // start a loop to be able to receive more than one DIMSE command
  while( cond == EC_Normal || cond == DIMSE_NODATAAVAILABLE || cond == DIMSE_OUTOFRESOURCES )
  {
    // receive a DIMSE command over the network
    cond = DIMSE_receiveCommand(assoc, DIMSE_BLOCKING, 0, &presID, &msg, &statusDetail);

    // if the command which was received has extra status
    // detail information, dump this information
    if (statusDetail != NULL)
    {
      delete statusDetail;
    }

    // check if peer did release or abort, or if we have a valid message
    if (cond == EC_Normal)
    {
      // in case we received a valid message, process this command
      // note that storescp can only process a C-ECHO-RQ and a C-STORE-RQ
      switch (msg.CommandField)
      {
        case DIMSE_C_ECHO_RQ:
          // process C-ECHO-Request
          cond = echoSCP(assoc, &msg, presID);
          break;
        case DIMSE_C_STORE_RQ:
          // process C-STORE-Request
          cond = storeSCP(assoc, &msg, presID);
          break;
        default:
          // we cannot handle this kind of message
          cond = DIMSE_BADCOMMANDTYPE;
          LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, tr("Cannot handle command: 0x%1.").arg((unsigned)msg.CommandField));
          break;
      }
    }
  }
  return cond;
}


OFCondition StoreScpThread::echoSCP( T_ASC_Association * assoc, T_DIMSE_Message * msg, T_ASC_PresentationContextID presID)
{
  OFString temp_str;

  /* the echo succeeded !! */
  OFCondition cond = DIMSE_sendEchoResponse(assoc, presID, &msg->msg.CEchoRQ, STATUS_Success, NULL);
  if (cond.bad())
  {
    DimseCondition::dump(temp_str, cond);
    LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, tr("Echo SCP Failed: %1.").arg(temp_str.c_str()));
  }
  return cond;
}

struct StoreCallbackData
{
  StudyRecord *study;
  DcmFileFormat* dcmff;
  T_ASC_Association* assoc;
  QString patientInfo;
};

static void
storeSCPCallback(
    void *callbackData,
    T_DIMSE_StoreProgress *progress,
    T_DIMSE_C_StoreRQ *req,
    char * /*imageFileName*/, DcmDataset **imageDataSet,
    T_DIMSE_C_StoreRSP *rsp,
    DcmDataset **statusDetail)
    /*
     * This function.is used to indicate progress when storescp receives instance data over the
     * network. On the final call to this function (identified by progress->state == DIMSE_StoreEnd)
     * this function will store the data set which was received over the network to a file.
     * Earlier calls to this function will simply cause some information to be dumped to stdout.
     *
     * Parameters:
     *   callbackData  - [in] data for this callback function
     *   progress      - [in] The state of progress. (identifies if this is the initial or final call
     *                   to this function, or a call in between these two calls.
     *   req           - [in] The original store request message.
     *   imageFileName - [in] The path to and name of the file the information shall be written to.
     *   imageDataSet  - [in] The data set which shall be stored in the image file
     *   rsp           - [inout] the C-STORE-RSP message (will be sent after the call to this function)
     *   statusDetail  - [inout] This variable can be used to capture detailed information with regard to
     *                   the status information which is captured in the status element (0000,0900). Note
     *                   that this function does specify any such information, the pointer will be set to NULL.
     */
{
  DIC_UI sopClass;
  DIC_UI sopInstance;

  // if this is the final call of this function, save the data which was received to a file
  // (note that we could also save the image somewhere else, put it in database, etc.)
  if (progress->state == DIMSE_StoreEnd)
  {
    // do not send status detail information
    *statusDetail = NULL;

    // remember callback data
    StoreCallbackData *cbdata = OFstatic_cast(StoreCallbackData *, callbackData);

    insertImageToDB(cbdata->dcmff, cbdata->study, cbdata->patientInfo);

      // check the image to make sure it is consistent, i.e. that its sopClass and sopInstance correspond
      // to those mentioned in the request. If not, set the status in the response message variable.
      if (rsp->DimseStatus == STATUS_Success)
      {
        // which SOP class and SOP instance ?
        if (!DU_findSOPClassAndInstanceInDataSet(*imageDataSet, sopClass, sopInstance))
        {
           rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
        }
        else if (strcmp(sopClass, req->AffectedSOPClassUID) != 0)
        {
          rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
        }
        else if (strcmp(sopInstance, req->AffectedSOPInstanceUID) != 0)
        {
          rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
        }
      }
  }
}


OFCondition StoreScpThread::storeSCP(
  T_ASC_Association *assoc,
  T_DIMSE_Message *msg,
  T_ASC_PresentationContextID presID)
    /*
     * This function processes a DIMSE C-STORE-RQ commmand that was
     * received over the network connection.
     *
     * Parameters:
     *   assoc  - [in] The association (network connection to another DICOM application).
     *   msg    - [in] The DIMSE C-STORE-RQ message that was received.
     *   presID - [in] The ID of the presentation context which was specified in the PDV which contained
     *                 the DIMSE command.
     */
{
  OFCondition cond = EC_Normal;
  T_DIMSE_C_StoreRQ *req;

  // assign the actual information of the C-STORE-RQ command to a local variable
  req = &msg->msg.CStoreRQ;

  // intialize some variables
  StoreCallbackData callbackData;
  callbackData.assoc = assoc;
  DcmFileFormat dcmff;
  callbackData.dcmff = &dcmff;
  StudyRecord study;
  callbackData.study = &study;
  const char *aet = 0;

  // store SourceApplicationEntityTitle in metaheader
  if (assoc && assoc->params)
  {
    aet = assoc->params->DULparams.callingAPTitle;
    if (aet) dcmff.getMetaInfo()->putAndInsertString(DCM_SourceApplicationEntityTitle, aet);
  }

  // define an address where the information which will be received over the network will be stored
  DcmDataset *dset = dcmff.getDataset();

  cond = DIMSE_storeProvider(assoc, presID, req, NULL, OFTrue, &dset,
    storeSCPCallback, &callbackData, DIMSE_BLOCKING, 0);

  // if some error occured, dump corresponding information and remove the outfile if necessary
  if (cond.bad())
  {
    OFString temp_str;
    DimseCondition::dump(temp_str, cond);
    QString msg = tr("Store SCP Failed: %1.").arg(temp_str.c_str());
    LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, msg);
    emit imageReceived(msg);
  } else {
      emit imageReceived(tr("Received: %1, From: %2, %3").arg(callbackData.patientInfo,
                                                              QString::fromLocal8Bit(aet),
                                                              QTime::currentTime().toString(SQLITE_TIME_FORMAT)));
  }

  // return return value
  return cond;
}


static
DUL_PRESENTATIONCONTEXT *
findPresentationContextID(LST_HEAD * head,
                          T_ASC_PresentationContextID presentationContextID)
{
  DUL_PRESENTATIONCONTEXT *pc;
  LST_HEAD **l;
  OFBool found = OFFalse;

  if (head == NULL)
    return NULL;

  l = &head;
  if (*l == NULL)
    return NULL;

  pc = OFstatic_cast(DUL_PRESENTATIONCONTEXT *, LST_Head(l));
  (void)LST_Position(l, OFstatic_cast(LST_NODE *, pc));

  while (pc && !found) {
    if (pc->presentationContextID == presentationContextID) {
      found = OFTrue;
    } else {
      pc = OFstatic_cast(DUL_PRESENTATIONCONTEXT *, LST_Next(l));
    }
  }
  return pc;
}


/** accept all presenstation contexts for unknown SOP classes,
 *  i.e. UIDs appearing in the list of abstract syntaxes
 *  where no corresponding name is defined in the UID dictionary.
 *  @param params pointer to association parameters structure
 *  @param transferSyntax transfer syntax to accept
 *  @param acceptedRole SCU/SCP role to accept
 */
static OFCondition acceptUnknownContextsWithTransferSyntax(
  T_ASC_Parameters * params,
  const char* transferSyntax,
  T_ASC_SC_ROLE acceptedRole)
{
  OFCondition cond = EC_Normal;
  int n, i, k;
  DUL_PRESENTATIONCONTEXT *dpc;
  T_ASC_PresentationContext pc;
  OFBool accepted = OFFalse;
  OFBool abstractOK = OFFalse;

  n = ASC_countPresentationContexts(params);
  for (i = 0; i < n; i++)
  {
    cond = ASC_getPresentationContext(params, i, &pc);
    if (cond.bad()) return cond;
    abstractOK = OFFalse;
    accepted = OFFalse;

    if (dcmFindNameOfUID(pc.abstractSyntax) == NULL)
    {
      abstractOK = OFTrue;

      /* check the transfer syntax */
      for (k = 0; (k < OFstatic_cast(int, pc.transferSyntaxCount)) && !accepted; k++)
      {
        if (strcmp(pc.proposedTransferSyntaxes[k], transferSyntax) == 0)
        {
          accepted = OFTrue;
        }
      }
    }

    if (accepted)
    {
      cond = ASC_acceptPresentationContext(
        params, pc.presentationContextID,
        transferSyntax, acceptedRole);
      if (cond.bad()) return cond;
    } else {
      T_ASC_P_ResultReason reason;

      /* do not refuse if already accepted */
      dpc = findPresentationContextID(params->DULparams.acceptedPresentationContext,
                                      pc.presentationContextID);
      if ((dpc == NULL) || ((dpc != NULL) && (dpc->result != ASC_P_ACCEPTANCE)))
      {

        if (abstractOK) {
          reason = ASC_P_TRANSFERSYNTAXESNOTSUPPORTED;
        } else {
          reason = ASC_P_ABSTRACTSYNTAXNOTSUPPORTED;
        }
        /*
         * If previously this presentation context was refused
         * because of bad transfer syntax let it stay that way.
         */
        if ((dpc != NULL) && (dpc->result == ASC_P_TRANSFERSYNTAXESNOTSUPPORTED))
          reason = ASC_P_TRANSFERSYNTAXESNOTSUPPORTED;

        cond = ASC_refusePresentationContext(params, pc.presentationContextID, reason);
        if (cond.bad()) return cond;
      }
    }
  }
  return EC_Normal;
}


/** accept all presenstation contexts for unknown SOP classes,
 *  i.e. UIDs appearing in the list of abstract syntaxes
 *  where no corresponding name is defined in the UID dictionary.
 *  This method is passed a list of "preferred" transfer syntaxes.
 *  @param params pointer to association parameters structure
 *  @param transferSyntax transfer syntax to accept
 *  @param acceptedRole SCU/SCP role to accept
 */
static OFCondition acceptUnknownContextsWithPreferredTransferSyntaxes(
  T_ASC_Parameters * params,
  const char* transferSyntaxes[], int transferSyntaxCount,
  T_ASC_SC_ROLE acceptedRole)
{
  OFCondition cond = EC_Normal;
  /*
  ** Accept in the order "least wanted" to "most wanted" transfer
  ** syntax.  Accepting a transfer syntax will override previously
  ** accepted transfer syntaxes.
  */
  for (int i = transferSyntaxCount - 1; i >= 0; i--)
  {
    cond = acceptUnknownContextsWithTransferSyntax(params, transferSyntaxes[i], acceptedRole);
    if (cond.bad()) return cond;
  }
  return cond;
}

static void insertImageToDB(DcmFileFormat *ff, StudyRecord *study, QString &patientName)
{
    DcmDataset *dset;
    QString dbFolder = mainWindow->getDbConfig().imagePath;

    if (ff && (dset=ff->getDataset()) && study) {
        const char *value = 0;
        QString studyUid, seriesUid, instUid, sopClassUid;
        dset->findAndGetString(DCM_StudyInstanceUID, value);
        studyUid = QString::fromLatin1(value);
        dset->findAndGetString(DCM_SeriesInstanceUID, value);
        seriesUid = QString::fromLatin1(value);
        dset->findAndGetString(DCM_SOPInstanceUID, value);
        instUid = QString::fromLatin1(value);
        dset->findAndGetString(DCM_SOPClassUID, value);
        sopClassUid = QString::fromLatin1(value);

        if (!(studyUid.isEmpty() || seriesUid.isEmpty() ||
              instUid.isEmpty() || sopClassUid.isEmpty())) {
            QString studyDirName;
            if (study->studyUid != studyUid) {
                study->studyUid = studyUid;
                dset->findAndGetString(DCM_AccessionNumber, value);
                study->accNumber = QString::fromLocal8Bit(value).remove(QChar(' '));
                dset->findAndGetString(DCM_PatientID, value);
                study->patientId = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_PatientName, value);
                study->patientName = QString::fromLocal8Bit(value);
                patientName = study->patientName;
                dset->findAndGetString(DCM_PatientSex, value);
                study->patientSex = QString::fromLocal8Bit(value).remove(QChar(' '));
                dset->findAndGetString(DCM_PatientBirthDate, value);
                study->patientBirth = QDate::fromString(QString::fromLatin1(value), "yyyyMMdd");
                dset->findAndGetString(DCM_PatientAge, value);
                study->patientAge = QString::fromLocal8Bit(value).remove(QChar(' '));
                dset->findAndGetString(DCM_AdmissionID, value);
                study->admissionId = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_PatientAddress, value);
                study->patientAddr = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_PatientTelephoneNumbers, value);
                study->patientPhone = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_PatientWeight, value);
                study->patientWeight = QString::fromLatin1(value);
                dset->findAndGetString(DCM_PatientSize, value);
                study->patientSize = QString::fromLatin1(value);
                dset->findAndGetString(DCM_MedicalAlerts, value);
                study->medicalAlerts = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_StudyDate, value);
                study->studyTime.setDate(QDate::fromString(QString::fromLatin1(value), "yyyyMMdd"));
                dset->findAndGetString(DCM_StudyTime, value);
                study->studyTime.setTime(formatDicomTime(QString::fromLatin1(value)));
                dset->findAndGetString(DCM_StudyDescription, value);
                study->studyDesc = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_ProtocolName, value);
                study->procId = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_RequestingPhysician, value);
                study->reqPhysician = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_PerformingPhysicianName, value);
                study->perPhysician = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_InstitutionName, value);
                study->institution = QString::fromLocal8Bit(value);
                dset->findAndGetString(DCM_Modality, value);
                study->modality = QString::fromLatin1(value);

                StudyDbManager::insertStudyToDb(*study, true);
                studyDirName = QString("%1/%2_%3").arg(study->studyTime.date().toString("yyyyMM"),
                                                                  study->studyTime.toString("yyyyMMddhhmmss"),
                                                                  study->accNumber);
                QDir().mkpath(QString("%1/%2").arg(dbFolder, studyDirName));
            }

            if (sopClassUid == UID_BasicTextSRStorage) {
                DSRDocument doc;
                OFCondition cond = doc.read(*dset);
                if (cond.good()) {
                    ReportRecord *report = new ReportRecord(instUid);
                    report->reportFile = QString("%1/%2_%3.dcm").arg(studyDirName, QString(REPORT_PREFIX), report->reportUid);
                    report->seriesUid = seriesUid;
                    report->studyUid = studyUid;
                    report->patientName = study->patientName;
                    report->patientSex = study->patientSex;
                    report->reqPhysician = study->reqPhysician;

                    value = doc.getInstanceCreationDate();
                    report->createTime.setDate(QDate::fromString(QString::fromLatin1(value), DATE_DICOM_FORMAT));
                    value = doc.getInstanceCreationTime();
                    report->createTime.setTime(formatDicomTime(QString::fromLatin1(value)));
                    value = doc.getContentDate();
                    report->contentTime.setDate(QDate::fromString(QString::fromLatin1(value), DATE_DICOM_FORMAT));
                    value = doc.getContentTime();
                    report->contentTime.setTime(formatDicomTime(QString::fromLatin1(value)));

                    report->isCompleted = (doc.getCompletionFlag()==DSRTypes::CF_Complete);
                    report->isVerified = (doc.getVerificationFlag()==DSRTypes::VF_Verified);

                    if (StudyDbManager::insertReportToDb(*report, true) ||
                            StudyDbManager::updateImageFile(report->reportUid, report->reportFile)) {
                        OFCondition cond = ff->saveFile(QString("%1/%2").arg(report->reportFile).toLocal8Bit().data(),
                                                        dset->getOriginalXfer(),
                                                        EET_ExplicitLength, EGL_recalcGL,
                                                        EPD_withoutPadding, 0, 0,EWM_fileformat);
                        if (cond.bad()) {
                            LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QObject::tr("Cannot write DICOM file: %1, Reason: %2.")
                                                            .arg(report->reportFile, cond.text()));
                        }
                    }

                    delete report;
                }
            } else {
               ImageRecord *image = new ImageRecord(instUid);
               image->sopClassUid = sopClassUid;
               image->seriesUid = seriesUid;
               image->studyUid = studyUid;
               bool raw = (sopClassUid==UID_DigitalXRayImageStorageForProcessing) ||
                       (sopClassUid==UID_DigitalMammographyXRayImageStorageForProcessing);
               image->imageFile = QString("%1/%2_%3.dcm").arg(studyDirName,
                                                              raw?QString(RAW_IMAGE_PREFIX):QString(PRESENT_IMAGE_PREFIX),
                                                              image->imageUid);

               dset->findAndGetString(DCM_ReferencedSOPInstanceUID, value, true);
               image->refImageUid = QString::fromLatin1(value);
               dset->findAndGetString(DCM_InstanceNumber, value);
               image->imageNo = QString::fromLatin1(value);
               dset->findAndGetString(DCM_BodyPartExamined, value);
               image->bodyPart = QString::fromLocal8Bit(value);
               dset->findAndGetString(DCM_SeriesDescription, value);
               image->imageDesc = QString::fromLocal8Bit(value);
               dset->findAndGetString(DCM_ContentDate, value);
               image->imageTime.setDate(QDate::fromString(QString::fromLatin1(value), DATE_DICOM_FORMAT));
               dset->findAndGetString(DCM_ContentTime, value);
               image->imageTime.setTime(formatDicomTime(QString::fromLatin1(value)));

               if (StudyDbManager::insertImageToDb(*image, true) ||
                       StudyDbManager::updateImageFile(image->imageUid, image->imageFile)) {
                   OFCondition cond = ff->saveFile(QString("%1/%2").arg(mainWindow->getDbConfig().imagePath, image->imageFile).toLocal8Bit().data(),
                                                   dset->getOriginalXfer(),
                                                   EET_ExplicitLength, EGL_recalcGL,
                                                   EPD_withoutPadding, 0, 0,EWM_fileformat);
                   if (cond.bad()) {
                       LogDbManager::insertMessageToDb(LogDbManager::ET_SysError, QObject::tr("Cannot write DICOM file: %1, Reason: %2.")
                                                       .arg(image->imageFile, cond.text()));
                   }
               }

               delete image;
             }
        }
    }
}
